﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;

namespace GBricks.Collections
{
  partial class Comparers
  {
    [Pure]
    public static IEqualityComparer<T> True<T>() {
      Contract.Ensures(Contract.Result<IEqualityComparer<T>>() != null);
      return ConstEqualityComparer<T>.True;
    }

    [Pure]
    public static IEqualityComparer<T> False<T>() {
      Contract.Ensures(Contract.Result<IEqualityComparer<T>>() != null);
      return ConstEqualityComparer<T>.False;
    }

    [Pure]
    public static IEqualityComparer<T> Const<T>(bool equals, int hash) {
      Contract.Ensures(Contract.Result<IEqualityComparer<T>>() != null);
      return new ConstEqualityComparer<T>(equals, hash);
    }

    [Pure]
    public static IComparer<T> Zero<T>() {
      Contract.Ensures(Contract.Result<IComparer<T>>() != null);
      return ConstComparer<T>.Zero;
    }

    [Pure]
    public static IComparer<T> Const<T>(int compare) {
      Contract.Ensures(Contract.Result<IComparer<T>>() != null);
      return new ConstComparer<T>(compare);
    }

    [Serializable]
    private sealed class ConstEqualityComparer<T> : EqualityComparer<T>
    {
      private static readonly ConstEqualityComparer<T> TrueInstance = new ConstEqualityComparer<T>(true, 0);
      private static readonly ConstEqualityComparer<T> FalseInstance = new ConstEqualityComparer<T>(false, 0);

      public ConstEqualityComparer(bool equals, int hash) {
        EqualsValue = equals;
        GetHashCodeValue = hash;
      }

      public static ConstEqualityComparer<T> True {
        [DebuggerStepThrough]
        get {
          Contract.Ensures(Contract.Result<ConstEqualityComparer<T>>() != null);
          return TrueInstance;
        }
      }

      public static ConstEqualityComparer<T> False {
        [DebuggerStepThrough]
        get {
          Contract.Ensures(Contract.Result<ConstEqualityComparer<T>>() != null);
          return FalseInstance;
        }
      }

      private bool EqualsValue { get; set; }

      private int GetHashCodeValue { get; set; }

      public override bool Equals(T x, T y) {
        return EqualsValue;
      }

      public override int GetHashCode(T obj) {
        return GetHashCodeValue;
      }

      public override bool Equals(object obj) {
        var other = obj as ConstEqualityComparer<T>;
        return other != null && other.EqualsValue == EqualsValue && other.GetHashCodeValue == GetHashCodeValue;
      }

      public override int GetHashCode() {
        return HashCode.Combine(EqualsValue.GetHashCode(), GetHashCodeValue);
      }
    }

    [Serializable]
    private sealed class ConstComparer<T> : Comparer<T>
    {
      private static readonly ConstComparer<T> ZeroInstance = new ConstComparer<T>(0);

      public ConstComparer(int compare) {
        CompareValue = compare;
      }

      public static ConstComparer<T> Zero {
        [DebuggerStepThrough]
        get {
          Contract.Ensures(Contract.Result<ConstComparer<T>>() != null);
          return ZeroInstance;
        }
      }

      private int CompareValue { get; set; }

      public override int Compare(T x, T y) {
        return CompareValue;
      }

      public override bool Equals(object obj) {
        var other = obj as ConstComparer<T>;
        return other != null && other.CompareValue == CompareValue;
      }

      public override int GetHashCode() {
        return CompareValue;
      }
    }
  }
}
